﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.IO;

namespace Flux3GUI
{
    public partial class Form1 : Form
    {
        private int portIndex;
        private object portName;
        private String EM1, EM2, EM3, EM4;
        private String transmitBuffer; /* 
                                        * 36 bits long (9 for each electromagnet)
                                        * 
                                        * 1st 9 bits: North Electromagnet
                                        * 2nd 9 bits: East Electromagnet
                                        * 3rd 9 bits: South Electromagnet
                                        * 4th 9 bits: West Electromagnet
                                        * 
                                        * 1 sign bit, 8 magnitude bits
                                        * 
                                        * Power States:
                                        *   Positive Polarity
                                        *       000000000 = + 0
                                        *       000000001 = +(1/255)
                                        *       000000010 = +(2/255)
                                        *       ...
                                        *       011111111 = +(1)
                                        *   Negative Polarity
                                        *       100000000 = - 0
                                        *       100000001 = -(1/255)
                                        *       100000010 = -(2/255)
                                        *       ...
                                        *       111111111 = -(1)
                                        * 
                                        */

        private String EM1state; // used to hold the state of the electromagnets
        private String EM2state; // differs from transmit buffer since it holds the current value, not the new value
        private String EM3state; // similar to a flip-flop (holds current state)
        private String EM4state;

        private SerialCommunication portCommunicator = new SerialCommunication(); //object to allow communication with ports
        private FileControl logger = new FileControl(); //object to allow for logging

        /*
         * Strings used for comparison by the recieve function to tell what state the CubeSat's are in
         * Sent by the cube upon reception of the transmission buffer
         * Can be expanded if necessary to encompass more states
         *  -add the states here
         *  -modify the recieve funciton
         */
        private const String SucessfulTransmissionIndicator = "110011"; //transmission sucessful
        private const String IllegalStateIndicator = "001100"; //transmission was an illegal EM configuration
        private Boolean messageRecieved = false;

        private const String Log_File_Name = "log.txt"; //filename of the logfile (remember to include the extension)


        public Form1()
        {
            InitializeComponent();
            
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //activate keyboard listening
            addKeyListener();
            this.KeyPreview = true;

            //set the combobox style to disallow typing in the box
            comboBox2.DropDownStyle = ComboBoxStyle.DropDownList;

            //populate the combobox with a list of the active ports
            portCommunicator.SetPortNameValues(comboBox2);

            //set up the recieved message listener
            portCommunicator.newRecievedMessage += new EventHandler(recieve);

            //set the transmit buffer textbox to be read only
            maskedTextBox1.ReadOnly = true;
            
            //initialize the transmit buffer
            initializeBuffer();

            //initialize the PortCommunicator
            initializePortCommunicator();

            //initialize logger
            initializeLogger();

            //MessageBox.Show("Its Working");

        }

        private void Form1_Close(object sender, FormClosingEventArgs e)
        {
         /* 
          * -closes any open ports
          * -turns off any half cubes that are still running
          *     -mostly the magnet half cube EM's
          * -needs to stop the logger/ close the log files
          */

            //use the E-Stop functionality
            EStop();

            //close the log files (form closes them automatically)
           logger.WriteToBeginningOfFile(Log_File_Name, "==GUI Closed==");

            //make sure the user wants to quit
            MessageBox.Show("Program is now quitting");
        }

        private void label1_Click(object sender, EventArgs e)
        {

        }

        private void initializeBuffer()
        {
            // initializes the buffer to all zeros
            EM1 = "000000000";
            EM2 = "000000000";
            EM3 = "000000000";
            EM4 = "000000000";
            updateBuffer();
        }

        private void initializePortCommunicator()
        {
            /*
             * NEED TO FIGURE OUT WHAT PARITY, STOPBITS, ETC SHOULD BE 
             * USED BASED ON THE EXPERIMENT LAPTOP/ ARDUINO
             * 
             */
            
            //set these when we know what they should be
            portCommunicator.Parity = "None";
            portCommunicator.StopBits = "One";
            portCommunicator.DataBits = "6"; //limit of bluetooth? max is 8 bits at a time? send 6x6?
            portCommunicator.BaudRate = "9600";
            ///////
            
            //sets the display to be the notification window of the GUI
            portCommunicator.DisplayWindow = richTextBox1;
        }

        private void initializeLogger()
        {
            /*
             * Method that handles the initialization of our logger class
             */

            logger.exceptionOutputWindow = richTextBox1;
        }

        private void comboBox2_SelectedIndexChanged(object sender, EventArgs e)
        {
            portIndex = comboBox2.SelectedIndex;
            portName = comboBox2.SelectedItem;
            
            //used to test the combo-box
            //maskedTextBox1.Text = portName.ToString();
        }

        private void splitContainer1_Panel2_Paint(object sender, PaintEventArgs e)
        {

        }

        private void maskedTextBox1_MaskInputRejected(object sender, MaskInputRejectedEventArgs e)
        {
            
        }

        private void button5_Click(object sender, EventArgs e)
        {
            //activate button
            //simply transmits whatever is currently in the transmit buffer
            transmit();
        }

        private void button6_Click(object sender, EventArgs e)
        {
            // deactivate button
            // sets all the EM's to off, and then transmits it
            EM1 = "000000000";
            EM2 = "000000000";
            EM3 = "000000000";
            EM4 = "000000000";
            updateBuffer();
            transmit();
        }

        private void button4_Click(object sender, EventArgs e)
        {
            //E-Stop button
            EStop();
        }


        #region Radio Button Control
        /*
         * Radio Button Control Below
         *  -change the transmit buffer string to what the radio button wants to accomplish
         *  -add more bits to the transmit buffer if needed
         *      -to get it to oscillate between two states?
         *      -pulsing of magnets on and off? for actuation
         *      -can add an extra  few bits that tells it how fast to oscillate?
         *          -or 1 bit that says either oscillate, or not
         *          -or no bits, and assume oscillation?
         */
        private void radioButton1_CheckedChanged(object sender, EventArgs e)
        {
            /*
             * Description:
             * 
             */
            if (radioButton1.Checked)
            {
                EM1 = "100000000";
                EM2 = "100000000";
                EM3 = "100000000";
                EM4 = "100000000";
                updateBuffer();
            }
        }
        private void radioButton2_CheckedChanged(object sender, EventArgs e)
        {
            /*
             * Description:
             * 
             */
            if (radioButton2.Checked)
            {
                EM1 = "000000100";
                EM2 = "000000100";
                EM3 = "000000100";
                EM4 = "000000100";
                updateBuffer();
            }
        }
        private void radioButton3_CheckedChanged(object sender, EventArgs e)
        {
            /*
             * Description:
             * 
             */
            if (radioButton3.Checked)
            {
                EM1 = "000011001";
                EM2 = "011010001";
                EM3 = "000111001";
                EM4 = "000011101";
                updateBuffer();
            }
        }
        private void radioButton4_CheckedChanged(object sender, EventArgs e)
        {
            /*
             * Description:
             * 
             */
            if (radioButton4.Checked)
            {
                EM1 = "100111111";
                EM2 = "100111000";
                EM3 = "100011000";
                EM4 = "101111000";
                updateBuffer();
            }
        }
        private void radioButton5_CheckedChanged(object sender, EventArgs e)
        {
            /*
             * Description:
             * 
             */
            if (radioButton5.Checked)
            {
                EM1 = "001100011";
                EM2 = "001100011";
                EM3 = "001100011";
                EM4 = "001100011";
                updateBuffer();
            }
        }
        private void radioButton6_CheckedChanged(object sender, EventArgs e)
        {
            /*
             * Description:
             * 
             */
            if (radioButton6.Checked)
            {
                EM1 = "111111111";
                EM2 = "111111111";
                EM3 = "111111111";
                EM4 = "111111111";
                updateBuffer();
            }
        }
        private void radioButton7_CheckedChanged(object sender, EventArgs e)
        {
            /*
             * Description:
             * 
             */
            if (radioButton7.Checked)
            {
                EM1 = "110000011";
                EM2 = "110000011";
                EM3 = "110000011";
                EM4 = "110000011";
                updateBuffer();
            }
        }
        private void radioButton8_CheckedChanged(object sender, EventArgs e)
        {
            /*
             * Description:
             * 
             */
            if (radioButton8.Checked)
            {
                EM1 = "100000001";
                EM2 = "100000001";
                EM3 = "100000001";
                EM4 = "100000001";
                updateBuffer();
            }
        }
        private void radioButton9_CheckedChanged(object sender, EventArgs e)
        {
            /*
             * Description:
             * 
             */
            if (radioButton9.Checked)
            {
                EM1 = "000010011";
                EM2 = "001100011";
                EM3 = "011100011";
                EM4 = "000011011";
                updateBuffer();
            }
        }
        private void radioButton10_CheckedChanged(object sender, EventArgs e)
        {
            /*
             * Description:
             * 
             */
            if (radioButton10.Checked)
            {
                EM1 = "111111111";
                EM2 = "111111111";
                EM3 = "000000000";
                EM4 = "000000000";
                updateBuffer();
            }
        }
        private void radioButton11_CheckedChanged(object sender, EventArgs e)
        {
            /*
             * Description:
             * 
             */
            if (radioButton11.Checked)
            {
                EM1 = "111111111";
                EM2 = "000000000";
                EM3 = "111111111";
                EM4 = "000000000";
                updateBuffer();
            }
        }
        private void radioButton12_CheckedChanged(object sender, EventArgs e)
        {
            /*
             * Description:
             * 
             */
            if (radioButton12.Checked)
            {
                EM1 = "000000000";
                EM2 = "000000000";
                EM3 = "111111111";
                EM4 = "111111111";
                updateBuffer();
            }
        }
        private void radioButton13_CheckedChanged(object sender, EventArgs e)
        {
            /*
             * Description:
             * 
             */
            if (radioButton13.Checked)
            {
                EM1 = "000000000";
                EM2 = "111111111";
                EM3 = "000000000";
                EM4 = "111111111";
                updateBuffer();
            }
        }
        #endregion


        #region Keyboard Listener

        private void addKeyListener()
        {
            this.KeyUp += new KeyEventHandler(this.ProcessKeyPress);
        }

        private void ProcessKeyPress(object sender, KeyEventArgs k)
        {
            /*
             * Listens to key strokes, and informs the program of them
             * When a key that is tied to something is pressed, that button's method is called
             * Works on:
             *  -preset configurations
             *  -transmit button
             *  -EStop button
             * 
             * -add more key functionality as I think them up
             * 
             */

            switch(k.KeyCode)
            {
                //begin radiobutton control
                case Keys.D1:
                    radioButton1.Checked = true;
                    k.Handled = true;
                    break;
                case Keys.D2:
                    radioButton2.Checked = true;
                    k.Handled = true;
                    break;
                case Keys.D3:
                    radioButton3.Checked = true;
                    k.Handled = true;
                    break;
                case Keys.D4:
                    radioButton4.Checked = true;
                    k.Handled = true;
                    break;
                case Keys.D5:
                    radioButton5.Checked = true;
                    k.Handled = true;
                    break;
                case Keys.D6:
                    radioButton6.Checked = true;
                    k.Handled = true;
                    break;
                case Keys.D7:
                    radioButton7.Checked = true;
                    k.Handled = true;
                    break;
                case Keys.D8:
                    radioButton8.Checked = true;
                    k.Handled = true;
                    break;
                case Keys.D9:
                    radioButton9.Checked = true;
                    k.Handled = true;
                    break;
                case Keys.A:
                    radioButton10.Checked = true;
                    k.Handled = true;
                    break;
                case Keys.B:
                    radioButton11.Checked = true;
                    k.Handled = true;
                    break;
                case Keys.C:
                    radioButton12.Checked = true;
                    k.Handled = true;
                    break;
                case Keys.D:
                    radioButton13.Checked = true;
                    k.Handled = true;
                    break;
                //end radiobutton control

                case Keys.Enter:
                    button5_Click(this, new EventArgs());
                    break;

                // add more case statements above
                case Keys.Escape:
                    Application.Exit();
                    break;
            }

        }

        #endregion

        private void updateState()
        {
            EM1state = EM1;
            EM2state = EM2;
            EM3state = EM3;
            EM4state = EM4;

            //log the new state and the time the state occured
            updateEMhistory();

            updateEMstatus();
        }

        private void updateBuffer()
        {
            transmitBuffer = EM1 + EM2 + EM3 + EM4;
            maskedTextBox1.Text = transmitBuffer;
        }

        private void updateEMstatus()
        {
            /*
             * Graphically display the status of the electromagnets
             *  -actually displays the state they "should be in"
             * Done by updating the progress bars "attached" to each electromagnet
             * Updates based on the internal (private) state variables of each Electromagnet
             * Also keeps a timestamped account of the status of all the EM's
             *  -can be used later for analysis
             *  -stored in a file
             *  -technically, only an account of what "should be happening"
             * State variable interfaces with: 
             *  -Receive function
             *  -Transmit function
             *  -EStop function
             * 
             */
            
            //South Progress Bar
            progressBar1.Value = calculateStatus(EM3state);
            if (EM3state.StartsWith("1"))
                label7.Text = "Negative";
            else
                label7.Text = "Positive";

            //West Progress Bar
            progressBar2.Value = calculateStatus(EM4state);
            if (EM4state.StartsWith("1"))
                label4.Text = "Negative";
            else
                label4.Text = "Positive";

            //North Progress Bar
            progressBar3.Value = calculateStatus(EM1state);
            if (EM1state.StartsWith("1"))
                label1.Text = "Negative";
            else
                label1.Text = "Positive";

            //East Progress Bar
            progressBar4.Value = calculateStatus(EM2state);
            if (EM2state.StartsWith("1"))
                label6.Text = "Negative";
            else
                label6.Text = "Positive";
            
            //update the history
            updateEMhistory();
        }

        private int calculateStatus(String EMstate)
        {
            /*
             * Calculates a value between 0 and 255 which corresponds to the magnitude of the EM
             * Converts the binary representation into a decimal number
             * Called by the updateEMstatus method to get a value for the progress bars
             * Does not include the sign bit in the conversion
             * 
             */
            string EMstateMagnitude = EMstate.Substring(1);
            long temp = Convert.ToInt64(EMstateMagnitude, 2);
            int result = (int)temp;
            return result;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            /*
             * This is called when the Open Port Button is clicked
             * -uses the selection from the combobox to pick which port to use
             * -uses the instantiated SerialCommunication object
             * -sets the text label accordingly
             * 
             */

            portCommunicator.PortName = (string)portName; //selected port from the combobox
            portCommunicator.OpenPort();

            //set the label to show the port as open
            label2.Text = "Status: Port Open";
        }

        private void button2_Click(object sender, EventArgs e)
        {
            /*
             * This is called when the Close Port Button is clicked
             * -uses the instantiated SerialCommunication object
             * -sets the text label accordingly
             * 
             */

            portCommunicator.ClosePort();

            //set the label to show the port as closed
            label2.Text = "Status: Port Closed";
        }

        private void transmit()
        {
            /*
             * Transmits a specified bit-string (from the transfer buffer) over the
             * currently open connection
             * Works on the Activate, Deactivate button clicks
             * Also keeps sending the message until acknowledgement is recieved
             */

            messageRecieved = false;

            //Keep transmitting the message until cube acknowledges that it is stopped
            Thread trd = new Thread(new ThreadStart(this.threadTransmissionTask));
            trd.IsBackground = true;
            trd.Start();
        }

        private void transmitEStop()
        {
            /*
             * Transmits for an E-Stop
             * Also closes the open port 
             * Also keeps sending the message until acknowledgement is recieved
             */

            messageRecieved = false;

            //Keep transmitting the message until cube acknowledges that it is stopped
            Thread trd = new Thread(new ThreadStart(this.threadTransmissionEStopTask));
            trd.IsBackground = true;
            trd.Start();
        }

        void recieve(object sender, EventArgs e)
        {
            /*
             * Receives handshake data from the half cubes over the open connection
             * Uses the PortCommunicator recievedMessage event
             * Updates the state variable of the EM's
             *  -to make sure the EMstatus matches up with the cubesat
             * 
             * Include information from the voltage feedback circuit we plan on having
             * 
             */

            String recievedMessage = portCommunicator.RecievedMessage;
            //check if transmission was sucessful
            switch (recievedMessage)
            {
                case SucessfulTransmissionIndicator:
                    //update state since transmission was sucessful
                    updateState();
                    messageRecieved = true;
                    break;
                case IllegalStateIndicator:
                    MessageBox.Show("Illegal EM State Error");
                    break;
                default:
                    MessageBox.Show("Unknown Error");
                    break;
            }
        }

        private void EStop()
        {
            /*
             * Emergency Stop function
             * If pressed, deactivates the electromagnets
             *  -do we need to "stop" any other halfcubes?
             * Works on a button click (big red button)
             * Updates the state variable of the EM's
             * Transmits the bit string that corresponds to "all off" for the EM's
             * 
             * -puts the "all off" bit string into the transmit buffer
             * -transmits it automatically
             * -does the same thing as deactivate as well as closes the port
             * 
             */

            //deactivate button functionality
            EM1 = "000000000";
            EM2 = "000000000";
            EM3 = "000000000";
            EM4 = "000000000";
            updateBuffer();

            //transmit the message
            transmitEStop();
        }

        private void threadTransmissionEStopTask()
        {
            while (portCommunicator.portOpened())
            {
                if (messageRecieved)
                { break; }
                else
                {
                    //attempt to write the transmission data
                    portCommunicator.WriteData(transmitBuffer);
                }
                //retry every 2 seconds
                Thread.Sleep(2000);
            }

            //port close button functionality
            portCommunicator.ClosePort();
            label2.Text = "Status: Port Closed";

            //set messageRecieved back to false to enable consistent functionality
            messageRecieved = false;
        }

        private void threadTransmissionTask()
        {
            /*
             * Method to control the sending helper thread
             * used by the transmit function to ensure message
             * delivery
             * 
             */

            while (portCommunicator.portOpened())
            {
                if (messageRecieved)
                { break; }
                else
                {
                    //attempt to write the transmission data
                    portCommunicator.WriteData(transmitBuffer);
                }
                //retry every 2 seconds
                Thread.Sleep(2000);
            }
        }

        private void updateEMhistory()
        {
            /*
             * Keeps a track of all the past EM configurations
             * Uses a timestamp
             * Allows for rough correlation of EM states to the collected data
             */

            
            string infoToLog = "New State At: " + System.DateTime.Now.ToShortDateString() + " || " + System.DateTime.Now.ToLongTimeString() + "\n"
                               + "North EM: " + convertEMStatusToPercent(EM1state) + "% power \n"
                               + "East EM: " + convertEMStatusToPercent(EM2state) + "% power \n"
                               + "South EM: " + convertEMStatusToPercent(EM3state) + "% power \n"
                               + "West EM: " + convertEMStatusToPercent(EM4state) + "% power \n";

            logger.WriteToBeginningOfFile(Log_File_Name, infoToLog);
        }

        private Double convertEMStatusToPercent(String EMStatus)
        {
            Double divisor = 255;
            Double percentage = (calculateStatus(EMStatus) / divisor) * 100;
            return Math.Round(percentage);
        }


        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            /*
             * About Menu Button functionality
             */

        }



        //////////////////////////////////
        //////Needed Functions Below://///
        //////////////////////////////////


        private void experimentSelect()
        {
            /*
             * Allows selection between Actuation and Docking modes
             * Deactivates parts of the GUI not needed for a particular mode
             * Works on a selection menu strip button click
             * 
             * -figure out how to use actionlistener/ if we want to
             * -figure out which parts of the GUI to have on/off
             *  -Docking Mode: only allows for transmission of activate/ deactivate commands
             *  -Actuation Mode: allows for EM presets to be used
             * 
             * -replace void function type if needed
             * -add input args if need be
             */
        }
       

        
        /*
         * Extra Things to Do:
         * 
         * -Voltage checking feature
         *      -add it to the recieve function?
         *      -make an indicator on the GUI?
         * -Implement the menu items I added to the GUI
         *      -exit button, about button, etc.
         * - Make a simulator for the program
         *      -connects via a virtual port
         *      -you can send it instructions and everything just like a real cube
         *      -the simulator keeps track of:
         *          -the commands it receives
         *          -the state of its magnets
         *      -nice GUI application
         *          -graphic representation of EM's
         *          -use the new WPF thing to make it
         * 
         */

    }
}
